﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
using Quote2015;
using Trade2015;

namespace data_server
{
	internal class Data
	{
		private Quote q;
		private Trade t;

		private readonly ConcurrentDictionary<string, TradeTimeInfo> _tradeTimes = new ConcurrentDictionary<string, TradeTimeInfo>();
		private string _tradingDay = string.Empty;
		private string _startDay = string.Empty;
		private string _preTime = string.Empty;
		private readonly ConcurrentQueue<string> _queueTicks = new ConcurrentQueue<string>();
		private string _tomorrowDay = string.Empty;
		private string _path;
		private System.Timers.Timer _timer = new System.Timers.Timer { Interval = 1000 };
		string oldTime = string.Empty;
		private string _outTime, _inTime;
		private bool _open;
		private Thread trd;
		private bool _needRelogin;

		~Data()
		{
			_timer.Stop();
			if (q != null && q.IsLogin)
			{
				q.ReqUserLogout();
				q = null;
			}
			if (t != null && t.IsLogin)
			{
				t.ReqUserLogout();
				t = null;
			}
		}

		public Data(string inTimer, string outTime)
		{
			trd = new Thread(write) {IsBackground = true};

			_outTime = outTime;
			_inTime = inTimer;

			_timer.Elapsed += _timer_Elapsed;
			_timer.Start();

			XmlDocument doc = new XmlDocument();
			doc.Load("TradingTime.xml");
			if (doc.DocumentElement != null)
				foreach (XmlNode proc in doc.DocumentElement.ChildNodes)
				{
					var tt = _tradeTimes.GetOrAdd(proc.Name, new TradeTimeInfo());
					foreach (XmlNode sec in proc.ChildNodes)
					{
						TimeSpan start = TimeSpan.Parse(sec.Attributes["start"].InnerText);
						TimeSpan end = TimeSpan.Parse(sec.Attributes["end"].InnerText);

						if (end < start) //隔夜
							end = end.Add(TimeSpan.FromDays(1));

						if (sec.Attributes["state"].InnerText == "open")
							tt.MatchTime = start.Add(TimeSpan.FromMinutes(-1)).ToString();
						else if (sec.Attributes["state"].InnerText == "close")
							tt.CloseTime = end.ToString();

						tt.EndTimes.Add(end.ToString());

						for (TimeSpan ts = start; ts < end; ts = ts.Add(TimeSpan.FromMinutes(1)))
							tt.Times.Add(ts.ToString()); //.ToString(@"hh\:mm\:ss"));
					}
				}
		}

		void write()
		{
			while (true)
			{
				string line;
				while (_queueTicks.TryDequeue(out line))
				{
					string inst = line.Split('|')[0];
					var dir = Directory.CreateDirectory(_path + "\\" + inst);
					File.AppendAllText(dir.FullName + "\\" + inst + "_" + _tradingDay + ".csv", line.Split('|')[1]);
				}
				Thread.Sleep(500);
			}
		}

		void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
		{
			if (t == null)
				return;

			//if (_needRelogin)
			//{
			//	_needRelogin = false;
			//	t.ReqUserLogout();
			//	if (q!=null && q.IsLogin)
			//	{
			//		q.ReqUserLogout();
			//	}
			//	Thread.Sleep(TimeSpan.FromMinutes(3));
			//	t.ReqUserLogin();
			//}

			//订阅行情
			if (t.IsLogin && q.IsLogin && q.DicTick.Count == 0)
			{
				foreach (var v in t.DicInstrumentField.Keys)
				{
					q.ReqSubscribeMarketData(v);
				}
			}

			//定时关闭
			if (_open)
				foreach (string tt in _outTime.Split(','))
				{//在3秒内
					if (DateTime.Now.TimeOfDay >= TimeSpan.Parse(tt) && DateTime.Now.TimeOfDay <= TimeSpan.Parse(tt).Add(TimeSpan.FromSeconds(3)))
					{
						if (q.IsLogin)
						{
							q.ReqUserLogout();
							q.DicTick.Clear(); //重启后行情再订阅
						}
						if (t.IsLogin)
							t.ReqUserLogout();
						_open = false;
						break;
					}
				}
			else
				//定时启动
				foreach (string tt in _inTime.Split(','))
				{//在3秒内
					if (DateTime.Now.TimeOfDay >= TimeSpan.Parse(tt) && DateTime.Now.TimeOfDay <= TimeSpan.Parse(tt).Add(TimeSpan.FromSeconds(3)))
					{
						if (!t.IsLogin)
							t.ReqConnect();

						if (!q.IsLogin)
						{
							q.DicTick.Clear();
							q.ReqConnect();
						}

						_open = true;
						break;
					}
				}
			string line;
			while (_queueTicks.TryDequeue(out line))
			{
				string inst = line.Split('|')[0];
				var dir = Directory.CreateDirectory(_path + "\\" + inst);
				File.AppendAllText(dir.FullName + "\\" + inst + "_" + _tradingDay + ".csv", line.Split('|')[1]);
			}

			if (oldTime != _preTime) //以分钟为单位
			{
				oldTime = _preTime;
				Console.Write(_preTime + ":00|");
			}
		}

		private void Log(string pFormat, params object[] p0)
		{
			Console.WriteLine(DateTime.Now.ToLongTimeString() + string.Format(pFormat, p0));
		}

		public bool Init(string svr, string user, string pwd, string path)
		{
			if (t == null)
				t = new Trade("ctp_trade_proxy.dll")
				{
					Server = svr.Split('|')[2],
					Broker = svr.Split('|')[1],
				};
			if (q == null)
				q = new Quote("ctp_quote_proxy.dll")
				{
					Server = svr.Split('|')[3],
					Broker = svr.Split('|')[1],
				};
			t.Investor = q.Investor = user;
			t.Password = q.Password = pwd;
			Directory.CreateDirectory(path);
			_path = path;
			q.OnFrontConnected += (sender, e) =>
			{
				Log("q.OnFrontConnected");
				q.ReqUserLogin();
			};
			q.OnRspUserLogin += (sender, e) =>
			{
				//重连时重新订阅
				if (t.DicInstrumentField.Count > 0)
				{
					foreach (var v in q.DicTick.Keys)
					{
						q.ReqUnSubscribeMarketData(v); //避免重复订阅
					}
					foreach (var v in t.DicInstrumentField.Keys)
					{
						q.ReqSubscribeMarketData(v);
						Directory.CreateDirectory(path + "\\" + v);
					}
				}
				Log("q.OnRspUserLogin:{0}", e.Value);
			};
			q.OnRspUserLogout += (sender, e) =>
			{
				Log("q.OnRspUserLogout:{0}", e.Value);
			};
			q.OnRtnError += (sender, e) => Log("q.OnRtnError:{0}=>{1}", e.ErrorID, e.ErrorMsg);
			q.OnRtnTick += q_OnRtnTick;


			t.OnFrontConnected += (sender, e) => t.ReqUserLogin();
			t.OnRspUserLogin += (sender, e) =>
			{
				Console.WriteLine("press q to quit.");
				Log("OnRspUserLogin:{0}", e.Value);
				if (e.Value == 0)
				{
					_tradingDay = t.TradingDay;
					_startDay = string.Empty; // DateTime.Today.ToString("yyyyMMdd");//用于处理actionday
					_tomorrowDay = string.Empty; // DateTime.ParseExact(_startDay, "yyyyMMdd", null).AddDays(1).ToString("yyyyMMdd");
				}
				else
					_tradingDay = "fail";
			};
			t.OnRspUserLogout += (sender, e) => Log("OnRspUserLogout:{0}", e.Value);
			t.OnRtnCancel += (sender, e) => Log("OnRtnCancel:{0}", e.Value.OrderID);
			t.OnRtnError += (sender, e) =>
			{
				Log("OnRtnError:{0}=>{1}", e.ErrorID, e.ErrorMsg);
				if (e.ErrorID == 90 || e.ErrorID == 7 || e.ErrorID == 8) //查询未准备,未初始化,前置不活跃
				{
					_needRelogin = true;
				}
			};
			t.OnRtnExchangeStatus += (sender, e) => Log("OnRtnExchangeStatus:{0}=>{1}", e.Exchange, e.Status);

			t.ReqConnect();
			//DateTime tNow = DateTime.Now;
			while (string.IsNullOrEmpty(_tradingDay))// !t.IsLogin && (DateTime.Now - tNow).TotalSeconds < 10)
				Thread.Sleep(1000);
			if (t.IsLogin)
			{
				q.ReqConnect();
				_open = true;
				//trd.Start(); //置于timer中处理
			}

			return t.IsLogin;
		}

		void q_OnRtnTick(object sender, TickEventArgs e)
		{
			//过滤Tick
			InstrumentField instField;
			if (!t.DicInstrumentField.TryGetValue(e.Tick.InstrumentID, out instField))
				return;
			string actionDay = t.TradingDay;
			//大商所夜盘,actionday为第2天日期,即tradingday
			string min = e.Tick.UpdateTime.Substring(0, 6) + "00";
			TradeTimeInfo tti;
			if (_tradeTimes.TryGetValue(instField.ProductID, out tti) || _tradeTimes.TryGetValue("default", out tti))
			{
				int minIndex = tti.Times.IndexOf(min);

				//确定startDay&tomorrowDay
				if (string.IsNullOrEmpty(_startDay))//每次登录成功后置空
				{
					if (DateTime.Now.TimeOfDay > TimeSpan.Parse(tti.CloseTime))
					{
						_startDay = DateTime.Today.ToString("yyyyMMdd");
						_tomorrowDay = DateTime.Today.AddDays(1).ToString("yyyyMMdd");
					}
					else
					{
						_tomorrowDay = DateTime.Today.ToString("yyyyMMdd");
						_startDay = DateTime.Today.AddDays(-1).ToString("yyyyMMdd");
					}
				}
				if (
					//正常行情
					(minIndex = tti.Times.IndexOf(min)) >= 0 || (minIndex = tti.Times.IndexOf("1." + min)) >= 0
					//开盘前竞价时间处理
					|| (minIndex = min == tti.MatchTime ? 0 : -1) == 0
					//休息时间(小节&收盘)
					|| (minIndex = tti.EndTimes.IndexOf(min) >= 0 ? tti.Times.IndexOf(TimeSpan.Parse(min).Add(TimeSpan.FromMinutes(-1)).ToString(@"hh\:mm\:ss")) : -1) >= 0
					)
				{
					int idx = tti.Times.IndexOf("1.00:00:00");
					if (idx >= 0) //有夜盘的品种
					{
						//处理actionday
						actionDay = minIndex < idx ? _startDay : _tomorrowDay;
					}
				}
				else
					return; //非交易时段行情不落地,否则会出现首tick日期不对的情况

			}

			//tick落地
			_queueTicks.Enqueue(e.Tick.InstrumentID + "|" + string.Format("{0:F3},{1:F3},{2},{3:F3},{4},{5:F3},{6},{7},{8:F3},{9:F3},{10},{11},{12},{13},{14}\r\n",
				e.Tick.LastPrice, e.Tick.BidPrice, e.Tick.BidVolume, e.Tick.AskPrice, e.Tick.AskVolume, e.Tick.AveragePrice, 0, 0, e.Tick.OpenInterest, e.Tick.Volume * instField.VolumeMultiple,
				e.Tick.Volume, e.Tick.UpdateTime, e.Tick.UpdateMillisec, actionDay, t.TradingDay));
			//File.AppendAllText(_tickPath + e.Tick.InstrumentID + ".csv", string.Format("{0:F3},{1:F3},{2},{3:F3},{4},{5:F3},{6},{7},{8:F3},{9:F3},{10},{11},{12},{13},{14}\r\n",
			//	e.Tick.LastPrice, e.Tick.BidPrice, e.Tick.BidVolume, e.Tick.AskPrice, e.Tick.AskVolume, e.Tick.AveragePrice, 0, 0, e.Tick.OpenInterest, e.Tick.Volume*instField.VolumeMultiple,
			//	e.Tick.Volume, e.Tick.UpdateTime.Split(' ')[1], e.Tick.UpdateMillisec, e.Tick.UpdateTime.Split(' ')[0], t.TradingDay));

			_preTime = e.Tick.UpdateTime.Substring(0, 5);
			//using (StreamWriter sw = new StreamWriter(new BufferedStream(new FileStream(, FileMode.Append))))
			//{
			//	//LastPrice,BidPrice1,BidVolume1,AskPrice1,AskVolume1,AveragePrice,HighestPrice,LowestPrice,OpenInterest,Turnover,Volume,UpdateTime,UpdateMillisec,ActionDay,TradingDay

			//	sw.WriteLine("{0:F3},{1:F3},{2},{3:F3},{4},{5:F3},{6},{7},{8:F3},{9:F3},{10},{11},{12},{13},{14}",
			//		e.Tick.LastPrice, e.Tick.BidPrice, e.Tick.BidVolume, e.Tick.AskPrice, e.Tick.AskVolume, e.Tick.AveragePrice, 0, 0, e.Tick.OpenInterest, e.Tick.Volume * instField.VolumeMultiple,
			//		e.Tick.Volume, e.Tick.UpdateTime.Split(' ')[1], e.Tick.UpdateMillisec, e.Tick.UpdateTime.Split(' ')[0], t.TradingDay);
			//}
		}
	}
}

